<script lang="ts">
export default {
  name: "xt-upload-avatar",
};
</script>

<script setup lang="ts">
import { computed, ref } from "vue";
import useRefHook from "../../../hooks/ref";
import { ElUpload, UploadFile, UploadFiles, UploadProps } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import xtIcon from "../../xt-icon/src/main.vue";
import xtAvatar from "../../xt-avatar/src/main.vue";
import { $singleMsg } from "../../../utils/element";
import Typings from "../../../typings";

const emit = defineEmits(["update:url", "error", "success"]);

const { INSTANCE, getInstance } = useRefHook();

interface PropType {
  action?: Typings.Upload.props["action"];
  accept?: Typings.Upload.props["accept"];
  fileSize?: number;
  size?: number;
  url?: string;
}

const props = withDefaults(defineProps<PropType>(), {
  action: "/file/upload",
  accept: ".png,.jpeg,.jpg,.gif", // 默认允许的上传文件类型
  fileSize: 1024 * 1024, // 1MB
  size: 100,
  url: "",
});

const uploadLoading = ref<number>(0);
const imageUrl = ref<string>("");

// 头像地址优先取 imageUrl, 没有则取默认的 src
const getSrc = computed<string>(() => {
  return imageUrl.value || props.url;
});

const onSuccess: UploadProps["onSuccess"] = (
  response,
  uploadFile,
  uploadFiles
) => {
  uploadLoading.value--;
  if (response.code !== 0) {
    return $singleMsg({ type: "error", message: response.msg });
  }
  imageUrl.value = response.data[0]?.url;
  emit("update:url", imageUrl.value);
  emit("success", response, uploadFile, uploadFiles);
};

const beforeUpload: UploadProps["beforeUpload"] = (rawFile) => {
  uploadLoading.value++;

  const { name, size } = rawFile;

  // 处理 accept 格式
  const accept = (props.accept || "")
    .toLowerCase()
    .replaceAll(".", "")
    .split(",");

  // 文件后缀名
  const etx = name.split(".").at(-1) || "";

  // 文件格式限制
  if (props.accept && !accept.includes(etx.toLowerCase())) {
    $singleMsg({
      type: "warning",
      message: `当前上传文件格式不符合，上传格式仅支持 ${props.accept}`,
    });
    uploadLoading.value--;
    return false;
  }

  // 文件大小限制
  if (props.fileSize && size > props.fileSize) {
    $singleMsg({
      type: "warning",
      message: `当前上传文件大小超出规定值，上传体积不能大于 ${Math.round(
        props.fileSize / 1024 / 1024
      )}MB`,
    });
    uploadLoading.value--;
    return false;
  }
};

// 文件上传失败时
const onError: UploadProps["onError"] = (error, UploadFilled, uploadFiles) => {
  uploadLoading.value--;
  emit("error", error, UploadFilled, uploadFiles);
};

defineExpose({
  getInstance,
});
</script>

<template>
  <el-upload
    class="xt-upload-avatar"
    :action="action"
    :accept="accept"
    v-loading="!!uploadLoading"
    :style="{ width: size + 'px', height: size + 'px' }"
    ref="INSTANCE"
    :show-file-list="false"
    :on-success="onSuccess"
    :before-upload="beforeUpload"
    :on-error="onError"
  >
    <xt-avatar
      v-if="getSrc"
      shape="square"
      :src="getSrc"
      :size="size"
      fit="fill"
    ></xt-avatar>
    <xt-icon
      v-else
      class="xt-upload-avatar-icon"
      :style="{ width: size + 'px', height: size + 'px' }"
      ><Plus
    /></xt-icon>
  </el-upload>
</template>

<style lang="scss">
@use "../../../scss/xt-upload-avatar.scss";
</style>
